/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Description: implement the cast source discovery
 * Create: 2024-05-22
 */
#include "wifi_display_manager.h"
#include "ipc_msg.h"
#include "wfd_source.h"
#include "cast_engine_log.h"
#include <set>
#include "utils.h"
#include "cast_ability_helper.h"
#include "power_mgr_client.h"
#include "radar_constants.h"

namespace OHOS {
namespace CastEngine {
namespace CastEngineService {
DEFINE_CAST_ENGINE_LABEL("Cast-Wifi-Display-Manager");

const std::string BUNDLENAME = "com.huawei.hmos.huaweicast";
const std::string ABILITYNAME = "CastSourceServiceAbility";

using namespace Sharing;
using namespace OHOS::PowerMgr;

class WifiDisplayListener : public IWfdEventListener {
public:
    explicit WifiDisplayListener(WifiDisplayManager* listener)
    {
        listener_ = listener;
    }

    virtual void OnInfo(std::shared_ptr<BaseMsg> &msg)
    {
        if (listener_) {
            listener_->OnInfo(msg);
        }
    }

private:
    WifiDisplayManager* listener_ = nullptr;
};

WifiDisplayManager &WifiDisplayManager::GetInstance()
{
    static WifiDisplayManager instance{};
    return instance;
}

void WifiDisplayManager::Init(std::shared_ptr<IWifiDisplayDeviceListener> listener)
{
    CLOGI("init");
    if (listener == nullptr) {
        CLOGE("The input listener is null!");
        return;
    }

    if (HasListener()) {
        CLOGE("Already inited");
        return;
    }

    SetListener(listener);
}

void WifiDisplayManager::DeInit()
{
    CLOGI("DeInit in");
    if (!HasListener()) {
        return;
    }

    {
        std::lock_guard<std::mutex> lock(deviceMutex_);
        devices_.clear();
    }
    StopDiscovery();
    ResetListener();
    sourceclient_.reset();
    sourceclient_ = nullptr;
}

void WifiDisplayManager::OnDied()
{
    CLOGI("OnDied");

    {
        std::lock_guard<std::mutex> lock(deviceMutex_);
        devices_.clear();
    }

    sourceclient_.reset();
}

bool WifiDisplayManager::RestartSource()
{
    CLOGI("RestartSource");

    if (sourceclient_ == nullptr) {
        sourceclient_ = WfdSourceFactory::CreateSource(0, "wfd_Source");
        if (!sourceclient_) {
            CLOGE("create wfd client error");
            return false;
        }
        auto wfdListener = std::make_shared<WifiDisplayListener>(this);
        wfdListener_ = wfdListener;
        sourceclient_->SetListener(wfdListener);
    }
    return true;
}

void WifiDisplayManager::StartDiscovery()
{
    CLOGI("StartDiscovery in");

    if (!HasListener()) {
        CLOGW("Not Init");
        return;
    }

    if (RestartSource()) {
        sourceclient_->StartDiscover();
    }
}

void WifiDisplayManager::StopDiscovery()
{
    CLOGI("StopDiscovery in");

    if (!HasListener()) {
        CLOGW("Not Init");
        return;
    }

    if (sourceclient_) {
        sourceclient_->StopDiscover();
    }
}

void WifiDisplayManager::AddCastDevice(const std::string deviceId)
{
    CLOGI("AddDevice In");

    if (!HasListener()) {
        CLOGW("Not Init");
        return;
    }

    WfdCastDeviceInfo deviceInfo;
    deviceInfo.deviceId = deviceId;
    if (sourceclient_) {
        if (sourceclient_->AddDevice(0, deviceInfo) == -1) {
            CLOGE("AddDevice error.\n");
            return;
        }
        SetWifiDisplayState(true);
    }
}

void WifiDisplayManager::RemoveCastDevice(const std::string deviceId)
{
    CLOGI("RemoveCastDevice In");

    if (!HasListener()) {
        CLOGW("Not Init");
        return;
    }

    if (sourceclient_) {
        if (sourceclient_->RemoveDevice(deviceId) == -1) {
            CLOGE("RemoveDevice error.\n");
        }
        SetWifiDisplayState(false);
    }
}

void WifiDisplayManager::SetAudioOutput(EndType type)
{
    CLOGI("SetAudioOutput In");

    if (!HasListener()) {
        CLOGW("Not Init");
        return;
    }

    if (sourceclient_) {
        if (type == EndType::CAST_SINK) {
            if (sourceclient_->SetAudioOutput(AudioOutput::AUTIO_OUTPUT_AT_SINK) == -1) {
                CLOGE("SetAudioOutput at sink error.\n");
            }
        } else if (type == EndType::CAST_SOURCE) {
            if (sourceclient_->SetAudioOutput(AudioOutput::AUTIO_OUTPUT_AT_SOURCE) == -1) {
                CLOGE("SetAudioOutput at source error.\n");
            }
        }
    }
}

void WifiDisplayManager::SetParamter(const std::string &param)
{
    CLOGI("SetParamter param %{public}s", param.c_str());
}

void WifiDisplayManager::RegisterStateListener(const std::shared_ptr<IWifiDisplayStateListener>& listener)
{
    std::lock_guard<std::mutex> lock(mutex_);
    stateListener_ = listener;
}

void WifiDisplayManager::UnregisterStateListener()
{
    std::lock_guard<std::mutex> lock(mutex_);
    stateListener_ = nullptr;
}

void WifiDisplayManager::OnInfo(std::shared_ptr<BaseMsg> &msg)
{
    CLOGI("OnInfo MsgId: %{public}d", msg->GetMsgId());

    switch (msg->GetMsgId()) {
        case WfdSourceDeviceFoundMsg::MSG_ID: {
            auto data = std::static_pointer_cast<WfdSourceDeviceFoundMsg>(msg);
            auto infos = data->deviceInfos;
            OnDeviceFound(infos);
            break;
        }
        case WfdConnectionChangedMsg::MSG_ID: {
            auto data = std::static_pointer_cast<WfdConnectionChangedMsg>(msg);
            ConnectionInfo info;
            info.ip = data->ip;
            info.mac = data->mac;
            info.state = static_cast<ConnectionState>(data->state);
            info.surfaceId = data->surfaceId;
            info.deviceName = data->deviceName;
            info.primaryDeviceType = data->primaryDeviceType;
            info.secondaryDeviceType = data->secondaryDeviceType;
            OnConnectionChanged(info);
            break;
        }
        case WfdErrorMsg::MSG_ID: {
            auto data = std::static_pointer_cast<WfdErrorMsg>(msg);
            OnError(data);
            break;
        }
        default:
            break;
    }
}

void WifiDisplayManager::OnError(std::shared_ptr<Sharing::WfdErrorMsg> &msg)
{
    CLOGE("errorCode: %{public}d, message is %{public}s",
          msg->errorCode,
          msg->message.c_str());

    int32_t errorCode = GetErrorCode(msg->errorCode);
    CLOGI("wifidisplay change errorCode to: %{public}d, ", errorCode);

    {
        std::unique_lock <std::mutex> lock(stateMutex_);
        if (isConnected_) {
            lock.unlock();
            if (stateListener_) {
                stateListener_->OnConnectionError(DeviceState::DISCONNECTED, currConnectionInfo.mac, errorCode);
            }
        }
    }
    currConnectionInfo.state = ConnectionState::DISCONNECTED;
    OnDeviceOffline();
    if (isEnablePowerForce_) {
        isEnablePowerForce_ = false;
        Utils::ResetPowerForceTimingOut();
        auto& powerMgrClient = PowerMgrClient::GetInstance();
        bool isScreenOn = powerMgrClient.IsScreenOn();
        CLOGI("isScreenOn: %{public}d", isScreenOn);
        if (!isScreenOn) {
            PowerErrors wakeupRet = powerMgrClient.WakeupDevice(WakeupDeviceType::WAKEUP_DEVICE_APPLICATION,
                                                                std::string("Miracast exit playing"));
            PowerErrors suspendRet = powerMgrClient.SuspendDevice();
            CLOGI("wakeupRet: %{public}d, suspendRet: %{public}d", wakeupRet, suspendRet);
        }
    }
    if (msg->errorCode == ERR_REMOTE_DIED) {
        OnDied();
    }

    SetWifiDisplayState(false);
}

void WifiDisplayManager::SetListener(const std::shared_ptr<IWifiDisplayDeviceListener>& listener)
{
    CLOGI("SetListener");

    std::lock_guard<std::mutex> lock(mutex_);
    deviceListener_ = listener;
}

bool WifiDisplayManager::HasListener()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return deviceListener_ != nullptr;
}

void WifiDisplayManager::ResetListener()
{
    SetListener(nullptr);
}

int32_t WifiDisplayManager::GetErrorCode(int32_t errorCode)
{
    return WIFI_DISPLAY_GENERAL_ERROR;
}

void WifiDisplayManager::OnDeviceOffline()
{
    CLOGI("OnDeviceOffline all");
    std::map<std::string, CastInnerRemoteDevice> devices;
    {
        std::lock_guard<std::mutex> lock(deviceMutex_);
        if (devices_.empty()) {
            CLOGI("devices_ is empty");
            return;
        }
        devices = std::move(devices_);
    }

    for (auto itDev : devices) {
        OnDeviceOffline(itDev.second.deviceId);
    }
    devices.clear();
}

void WifiDisplayManager::OnDeviceOffline(const std::string &deviceId)
{
    CLOGI("OnDeviceOffline device id %{public}s", Utils::Mask(deviceId).c_str());

    if (deviceListener_) {
        deviceListener_->OnDeviceOffline(deviceId);
    }
}

void WifiDisplayManager::OnConnectionChanged(const ConnectionInfo &info)
{
    CLOGI("OnConnectionChanged deviceName %{public}s, state:%{public}d",
        Utils::Mask(info.deviceName).c_str(), info.state);
	currConnectionInfo = info;
    if (info.state == ConnectionState::CONNECTED) {
        if (stateListener_) {
            stateListener_->OnConnectionChanged(DeviceState::CONNECTED, info.mac, info.deviceName);
        }
        connection_ = new (std::nothrow) CastAbilityConnection();
        if (connection_ != nullptr) {
            int ret = CastAbilityHelper::ConnectServiceExtensionAbility(BUNDLENAME, ABILITYNAME, connection_);
            if (ret != 0) {
                CLOGI("connect huaweicast fail, error code: %{public}d", ret);
            } else {
                CLOGI("connect huaweicast success");
            }
        }
        Utils::EnablePowerForceTimingOut();
        isEnablePowerForce_ = true;
    } else if (info.state == ConnectionState::DISCONNECTED) {
        if (stateListener_) {
            stateListener_->OnConnectionChanged(DeviceState::DISCONNECTED, info.mac, info.deviceName);
        }
        SetWifiDisplayState(false);
        if (connection_ != nullptr) {
            int ret = CastAbilityHelper::DisconnectServiceExtensionAbility(connection_);
            if (ret != 0) {
                CLOGI("disconnect huaweicast fail, error code: %{public}d", ret);
            } else {
                CLOGI("disconnect huaweicast success");
            }
        }
        if (isEnablePowerForce_) {
            isEnablePowerForce_ = false;
            Utils::ResetPowerForceTimingOut();
            auto& powerMgrClient = PowerMgrClient::GetInstance();
            bool isScreenOn = powerMgrClient.IsScreenOn();
            CLOGI("isScreenOn: %{public}d", isScreenOn);
            if (!isScreenOn) {
                powerMgrClient.WakeupDevice(WakeupDeviceType::WAKEUP_DEVICE_APPLICATION,
                                            std::string("Miracast exit playing"));
                powerMgrClient.SuspendDevice();
            }
        }
    } else if (info.state == ConnectionState::CONNECTING) {
        if (stateListener_) {
            stateListener_->OnConnectionChanged(DeviceState::CONNECTING, info.mac, info.deviceName);
        }
    } else if (info.state == ConnectionState::PLAYING) {
        if (stateListener_) {
            stateListener_->OnConnectionChanged(DeviceState::PLAYING, info.mac, info.deviceName);
        }
    }
}

void WifiDisplayManager::OnDeviceFound(const std::vector<WfdCastDeviceInfo> &deviceInfos)
{
    if (!deviceListener_) {
        CLOGI("no listener.\n");
        return;
    }

    std::set<std::string> deleteSet;
    std::vector<CastInnerRemoteDevice> vecDevices;
    {
        std::lock_guard<std::mutex> lock(deviceMutex_);
        for (auto dev : devices_) {
            deleteSet.insert(dev.second.deviceId);
        }

        for (auto devInfo : deviceInfos) {
            CastInnerRemoteDevice device{};
            device.deviceId = devInfo.deviceId;
            device.deviceName = devInfo.deviceName;
            device.ipAddress = devInfo.ipAddr;
            device.isLeagacy = true;
            device.protocolType = ProtocolType::MIRACAST;
            device.deviceType = DeviceType::DEVICE_MIRACAST;

            if (devices_.find(devInfo.deviceId) != devices_.end()) {
                deleteSet.erase(devInfo.deviceId);
            } else {
                devices_[devInfo.deviceId] = device;
            }
            vecDevices.emplace_back(device);
            CLOGI("OnDeviceFound device id<%{public}s-name:%{public}s>", Utils::Mask(device.deviceId).c_str(),
                Utils::Mask(device.deviceName).c_str());
        }
    }
    if (deleteSet.size() > 0) {
        for (auto dev : deleteSet) {
            std::lock_guard<std::mutex> lock(deviceMutex_);
            devices_.erase(dev);
            OnDeviceOffline(dev);
        }
    }

    if (deviceListener_) {
        if (!vecDevices.empty()) {
            deviceListener_->OnDeviceFound(vecDevices);
        }
    }
    CLOGI("OnDeviceFound size: %{public}d", vecDevices.size());
}

void WifiDisplayManager::SetWifiDisplayState(bool connect)
{
    std::lock_guard<std::mutex> lock(stateMutex_);
    isConnected_ = connect;
}

std::shared_ptr<Sharing::WfdSink>& WifiDisplayManager::GetWfdSink()
{
    if (sinkClient_) {
        return sinkClient_;
    }

    sinkClient_ = WfdSinkFactory::CreateSink(0, "wfd_Sink");
    return sinkClient_;
}

bool WifiDisplayManager::SetSinkDiscoverable(bool isDiscoverable)
{
    int32_t ret = isDiscoverable ? GetWfdSink()->Start() : GetWfdSink()->Stop();
    if (ret < 0) {
        CLOGE("%{public}s fail of wifi_display", isDiscoverable ? "Start" : "Stop");
    }

    return ret >= 0;
}

bool WifiDisplayManager::RegisterSinkEventListener(const std::shared_ptr<IWfdEventListener> &listener)
{
    if (listener == nullptr) {
        CLOGE("listener is null");
        return false;
    }

    return GetWfdSink()->SetListener(listener) == 0;
}

bool WifiDisplayManager::AcceptDeviceConnection(const std::string &deviceId, bool accept)
{
    return GetWfdSink()->Acception(deviceId, accept) == 0;
}

bool WifiDisplayManager::Play(const std::string &deviceId)
{
    return GetWfdSink()->Play(deviceId) == 0;
}

bool WifiDisplayManager::Pause(const std::string &deviceId)
{
    return GetWfdSink()->Pause(deviceId) == 0;
}

bool WifiDisplayManager::SetSurface(const std::string &deviceId, sptr<IBufferProducer> producer)
{
    return GetWfdSink()->AppendSurface(deviceId, producer) == 0;
}

bool WifiDisplayManager::RemoveSurface(const std::string &deviceId, uint64_t surfaceId)
{
    return GetWfdSink()->RemoveSurface(deviceId, surfaceId) == 0;
}

bool WifiDisplayManager::Close(const std::string &deviceId)
{
    return GetWfdSink()->Close(deviceId) == 0;
}

} // namespace CastEngineService
} // namespace CastEngine
} // namespace OHOS
